﻿#region File Description
/*-----------------------------------------------------------------------------
 * Class: EmployeeManager
 *
 *  // mediator pattern? includes both ui and functionality in one class
 *
 *
 * Notes: Employee will be a base class for specific employees, if we get there.
 -------------------------------------------------------------------------------*/
#endregion

using System;
using System.Collections.Generic;
using Cubezicles.Employees;
using Cubezicles.UI;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace Cubezicles
{
    /// <summary>
    /// Manages all the employees in the game, and displays the list of employees
    /// Also determines the initial placement of employees and their cubicles
    /// </summary>
    public class EmployeeManager : DrawableGameComponent
    {
        public static readonly
            Node aisleBL = new Node(new Vector3(-16f, 0f, 9f)),
                 aisleTL = new Node(new Vector3(-16f, 0f, -18f)),
                 aisleBR = new Node(new Vector3(5.5f, 0f, 9f)),
                 aisleTR = new Node(new Vector3());

        // this is hacky but lizz is lazy...therefore copying off of itemmanager style
        // Reference objects (Maybe there's a better way to do this..........)
        // make new instances at beginning but dont add them to needobjects until bought
        internal Employee dev, leaddev, slacker, steve;

        // Get the Game1 object stored by the base class
        private Game1 game { get { return (Game1)Game; } }

        // All hired employees
        // List of all employees in the game (needs to be able to be appended to.... or set limit here?)
        public List<Employee> Employees = new List<Employee>();

        // "ID" is just the location in the array
        //....probably a wayyy more elegant way to do this but cheaty for now
        public int NextEmployeeID { get { return Employees.Count; } }

        // Total time worked by employees - used to calculate revenue at end of day
        public float TotalTimeWorked { get; set; } //......

        public UI.EmployeeManagerPane managerPane { get; private set; }
        public UI.HireNewEmployeeManager newHireManagerPane { get; private set; }

        #region EmployeeFileNames

        internal const String devModelFileName = "Models/Employees/Dev/dev";
       // const String devFaceFileName = "Models/Employees/Dev/face";

        internal const String leadDevModelFileName = "Models/Employees/LeadDev/leadDev";
       // const String leadDevFaceFileName = ""; //"Models/Employees/LeadDev/leadface";

        internal const String slackerModelFileName = "Models/Employees/Slacker/slacker";
        //const String slackerFaceFileName = ""; //"Models/Employees/Slacker/slackerface";

        internal const String steveModelFileName = "Models/Employees/Steve/steve";
        //const String steveFaceFileName = ""; //"Models/Employees/Steve/steveface";
        #endregion

        // Employee names, see Content/Config/EmployeeNames.xml              
        private List<String> employeeNamesF; // female names
        private List<String> employeeNamesM; // male names

        // Cubicle positions, see Content/Config/CubiclePositions.xml
        private List<Vector3> cubiclePositions;

        /// <summary>
        /// EmployeeManager class constructor.
        /// </summary>
        public EmployeeManager(Game1 game): base(game)
        {
            this.DrawOrder = (int)Game1.DisplayOrder.Menus;

            newHireManagerPane = new UI.HireNewEmployeeManager(game, this); 
            managerPane = new EmployeeManagerPane(game, this);

            //lalalal bad coding standards just ignore meee......
            // yep i relaly did make a shorter constructor for hacky purposes hopefully everythign doesnt die
            dev = new Dev(game, EmployeeManager.devModelFileName);
            leaddev = new LeadDev(game, EmployeeManager.leadDevModelFileName);
            slacker = new Slacker(game, EmployeeManager.slackerModelFileName);
            steve = new Steve(game, EmployeeManager.steveModelFileName);
           
        }

        protected override void LoadContent()
        {
            game.Components.Add(managerPane);
            game.Components.Add(newHireManagerPane);

            // Load the employee names and positions from our config files
            cubiclePositions = Game.Content.Load<List<Vector3>>("Config/CubiclePositions");
            employeeNamesF = Game.Content.Load<List<string>>("Config/FemaleEmployeeNames");
            employeeNamesM = Game.Content.Load<List<string>>("Config/MaleEmployeeNames");


            setGraph();

            // Add one starter employee
            // Initially add one employee+cubicle to game
            AddEmployee(EmployeeType.Dev);

            base.LoadContent();
        }

        // for testing!! ................
        public void AddDev() { AddEmployee(EmployeeType.Dev);}
        public void AddLeadDev() { AddEmployee(EmployeeType.LeadDev);}
        public void AddSteve() { AddEmployee(EmployeeType.Steve);}
        public void AddSlacker() { AddEmployee(EmployeeType.Slacker);}
        // end for testing

        public enum EmployeeType { Dev, LeadDev, Slacker, Steve, UXDesigner }

        /// <summary>
        /// Add an employee (doesn't involve money at all)
        /// </summary>
        /// <param name="employeeType"></param>
        private void AddEmployee(EmployeeType employeeType)
        {
            
            Cubicle c = new Cubicle(this.game, cubiclePositions[NextEmployeeID], NextEmployeeID);
            Employee employeeToAdd = null; // new Employee(this.game, employeeNames[NextEmployeeID], NextEmployeeID, c, modelFileName);

            switch(employeeType)
            {
                case EmployeeType.Dev:
                    employeeToAdd = new Dev(game, employeeNamesM[NextEmployeeID], NextEmployeeID, c, devModelFileName);
                    break;
                case EmployeeType.LeadDev:
                    employeeToAdd = new LeadDev(game, employeeNamesF[NextEmployeeID], NextEmployeeID, c, leadDevModelFileName);
                    break;
                case EmployeeType.Slacker:
                    employeeToAdd = new Slacker(game, employeeNamesM[NextEmployeeID], NextEmployeeID, c, slackerModelFileName);
                    break;
                case EmployeeType.Steve:
                    employeeToAdd = new Steve(game, employeeNamesM[NextEmployeeID], NextEmployeeID, c, steveModelFileName);
                    break;
                case EmployeeType.UXDesigner:
                    employeeToAdd = new UXDesigner(game, employeeNamesF[NextEmployeeID], NextEmployeeID, c, steveModelFileName);
                    break;
                default:
                    employeeToAdd = new Dev(game, employeeNamesM[NextEmployeeID], NextEmployeeID, c, devModelFileName);
                    break;
            }

            Employees.Add(employeeToAdd);

            // Depending on the employee ID, have different locations of cubicles
            // and might need to rotate the cubicle (and the employee inside)
            if (NextEmployeeID == 3 || NextEmployeeID == 4 || NextEmployeeID == 8 || NextEmployeeID == 7)
            {
                c.Rotate90CW();
                c.Rotate90CW();
                employeeToAdd.Rotate90CW();
                employeeToAdd.Rotate90CW();
            }

            // Put the employee at the door
            employeeToAdd.Position = ndoor.pos;
            employeeToAdd.SrcNode = ndoor;

            // Add these DrawableGameComponents to the game
            Game.Components.Add(employeeToAdd);
            Game.Components.Add(c);
        }


        public void AddAllEmployees()
        {
            // already placed 0 at initialization
            for (int i = 1; i < 10; i++)
            {
                AddEmployee(EmployeeType.Dev);
                //EmployeeManager.Hire();
            }
        }


        



        // todo: better way to get employees in list....
        // gets funky if you fire and rehire same person, etc.... associated names and cubicle locations

        // returns whether or not the hire was successful
        /// <summary>
        /// Hires the specified employee type.
        /// </summary>
        /// <param name="employeeType">Type of the employee.</param>
        /// <returns></returns>
        public bool Hire(EmployeeType employeeType)
        {
            // First, check to see if we have reached our limit of employees 
            if (NextEmployeeID >= 10) return false;


            // Check to see if there are sufficient funds to hire an employee type
            // If there are sufficient funds, hire them
            if (game.MoneyManager.BuyHireBonus(employeeType))
            {
                AddEmployee(employeeType); // all other money stuff handled by moneymanager
 
                return true;
            }

            // Failure to hire a new employee
            return false;
        }

        /// <summary>
        /// Fires an employee.
        /// </summary>
        /// <param name="emp">Employee object.  Must not be null.</param>
        /// <returns></returns>
        public bool Fire(Employee emp)
        {
            // If the employee to fire is the currently selected employee, deselect them first
            if (emp.IsSelected) game.CurrentEmployee = null;

            Employees.Remove(emp);

            game.Components.Remove(emp);
            game.Components.Remove(emp.cubicle);
            
            // todo: not sure if this (fire) covers all of the relevant variables.....

            return true;
        }

        /// <summary>
        /// Reset all employee needs levels to 100% (called at start of each day)
        /// </summary>
        public void ResetAllEmployees()
        {
            Employees.ForEach(e => e.ResetEmployee());
        }

        // Tab key to toggle between currently selected employee
        // no longer working?!?!? ****
        // todo: is this delcared twice??
       /* public override void Update(GameTime gameTime)
        {
            KeyboardState ks = Keyboard.GetState();

            if (game.oldks.IsKeyDown(Keys.Tab) && game.ks.IsKeyUp(Keys.Tab))
            {
                NextEmployee();
            }
        }*/

        /// <summary>
        /// Cycle the selected employee to the next in our list
        /// </summary>
        public void NextEmployee() //todo: make sure this is right?
        {
            int index = (Employees.IndexOf(game.CurrentEmployee) + 1) % Employees.Count;
            game.CurrentEmployee = null;
            game.CurrentEmployee = Employees[index];
        }

        /// <summary>
        /// Tell all the employees to go home
        /// </summary>
        public void EmpGoHomeAlready() //todo: make sure this is right?
        {
            Employees.ForEach(e => e.setDestination(ndoor));
        }

        #region pathfinding

        /// <summary>
        /// Draw the pathfinder Graph
        /// </summary>
        public Node ndoor;
        private void setGraph()
        {
            game.graphi = game.gra.nodes.Count;
            Vector3 noff = new Vector3(-1, 0, 9);
            Vector3 noffTop = new Vector3(2, 0, -9);
            Vector3 noffRight = new Vector3(11, 0, 0);
            Vector3 nOffLeft = new Vector3(-11, 0, 0);
            Node n1, n2, n3, n4, n5, n6, n7, n8, n9, n0, //n0-9 are the centers of the cubicles where the employees sit and work
                n11, n12, n13, n14, n15, n16, n17, n18, n19, n10, //n10-19 should be the positions right outside the cubicles.
                ntl, nbl, ntm, ntr, nbr; //These are the intermediary nodes..... ntr and nbr should be by the bathroom
            n0 = new Node(cubiclePositions[0]); 
            n1 = new Node(cubiclePositions[1]);
            n2 = new Node(cubiclePositions[2]);
            n3 = new Node(cubiclePositions[3]);
            n4 = new Node(cubiclePositions[4]);
            n5 = new Node(cubiclePositions[5]);
            n6 = new Node(cubiclePositions[6]);
            n7 = new Node(cubiclePositions[7]);
            n8 = new Node(cubiclePositions[8]);
            n9 = new Node(cubiclePositions[9]);
            n10 = new Node(n0.pos + noff);
            n11 = new Node(n1.pos + noff);
            n12 = new Node(n2.pos + noffTop);
            n13 = new Node(n3.pos + noffTop);
            n14 = new Node(n4.pos + noff);
            n15 = new Node(n5.pos + noff);
            n16 = new Node(n6.pos + noffTop);
            n17 = new Node(n7.pos + noffTop);
            n18 = new Node(n8.pos + noff);
            n19 = new Node(n9.pos + noff);
            ntl = new Node(n17.pos + nOffLeft);
            nbl = new Node(n15.pos + nOffLeft);
            ntm = new Node(new Vector3((n18.pos.X + n19.pos.X) / 2, n1.pos.Y, n16.pos.Z + 2));
            ntr = new Node(n13.pos + noffRight);
            nbr = new Node(n11.pos + noffRight);
            ndoor = new Node(new Vector3(28, 0, 40));

            //This order is very important. If you're adding nodes, append it.
            game.gra.addNode(n0); //id = 0
            game.gra.addNode(n1); //id = 1
            game.gra.addNode(n2); //id = 2
            game.gra.addNode(n3); //id = 3
            game.gra.addNode(n4); //id = 4
            game.gra.addNode(n5); //id = 5
            game.gra.addNode(n6); //id = 6
            game.gra.addNode(n7); //id = 7
            game.gra.addNode(n8); //id = 8
            game.gra.addNode(n9); //id = 9
            game.gra.addNode(n10); //id = 10
            game.gra.addNode(n11); //id = 11
            game.gra.addNode(n12); //id = 12
            game.gra.addNode(n13); //id = 13
            game.gra.addNode(n14); //id = 14
            game.gra.addNode(n15); //id = 15
            game.gra.addNode(n16); //id = 16
            game.gra.addNode(n17); //id = 17
            game.gra.addNode(n18); //id = 18
            game.gra.addNode(n19); //id = 19
            game.gra.addNode(ntm); //id = 20
            game.gra.addNode(ntl); //id = 21
            game.gra.addNode(nbl); //id = 22
            game.gra.addNode(ntr); //id = 23
            game.gra.addNode(nbr); //id = 24
            game.gra.addNode(ndoor); //Magic Door
            game.gra.addNode(aisleBL);
            game.gra.addNode(aisleTL);

            //Connecting Employee at work to outside their cubicles
            game.gra.addEdge(n1, n11);
            game.gra.addEdge(n2, n12);
            game.gra.addEdge(n3, n13);
            game.gra.addEdge(n4, n14);
            game.gra.addEdge(n5, n15);
            game.gra.addEdge(n6, n16);
            game.gra.addEdge(n7, n17);
            game.gra.addEdge(n8, n18);
            game.gra.addEdge(n9, n19);
            game.gra.addEdge(n0, n10);
            //Horizontal Edges
            //Top Row
            game.gra.addEdge(n17, ntl);
            game.gra.addEdge(n17, n16);
            game.gra.addEdge(n16, aisleTL);
            game.gra.addEdge(aisleTL, ntm);
            game.gra.addEdge(ntm, n12);
            game.gra.addEdge(n12, n13);
            game.gra.addEdge(n13, ntr);
            //Bottom Row
            game.gra.addEdge(n15, nbl);
            game.gra.addEdge(n15, n14);
            game.gra.addEdge(n14, n18);
            game.gra.addEdge(n18, n19);
            game.gra.addEdge(n19, aisleBL);
            game.gra.addEdge(aisleBL, n10);
            game.gra.addEdge(n10, n11);
            game.gra.addEdge(n11, nbr);
            //Vertical Edges
            game.gra.addEdge(ntl, nbl);
            game.gra.addEdge(ntr, nbr);
            game.gra.addEdge(aisleBL, aisleTL);
            //Add magical door.
            game.gra.addEdge(ndoor, nbr);
        }

        #endregion


        // wy won't you woookrrrrkrkkrkrk
        public void HireNew()
        {
            newHireManagerPane.IsVisible = true;
        }




        // **** reset =  fire everyone? remove everyone? not sure for memory/quality of code...
        /// <summary>
        /// The game was reset. Get rid of all but starter employee.
        /// </summary>
        public void Reset()
        {
            if (Employees.Count > 0)
            {
                // this assumes that the logic behind fire is sound! ***
                for (int i = 0; i < Employees.Count; i++)
                {
                    game.Components.Remove(Employees[i]);
                    game.Components.Remove(Employees[i].cubicle);
                    Fire(Employees[i]);
                }

                // just double check that employee list is empty
                Employees.Clear();
            }

            // Add one starter employee
            AddEmployee(EmployeeType.Dev);
        }
    }
}
